package thread;


import java.util.concurrent.TimeUnit;

/**
 * <a href="https://juejin.cn/post/6844903520437551111">...</a>
 * 输出结果为：
 * 进入消费者线程
 * wait flag 1:false
 * 还没生产，进入等待
 * 进入生产者线程
 * 生产
 * 退出生产者线程
 * 结束等待 wait flag 2:true
 * 消费
 * 退出消费者线程
 * <p>
 * 理解了输出结果的顺序，也就明白了wait/notify的基本用法。有以下几点需要知道：
 * 1. 在示例中没有体现但很重要的是，wait/notify方法的调用必须处在该对象的锁（Monitor）中，也即，在调用这些方法时首先需要获得该对象的锁。否则会抛出IllegalMonitorStateException异常。
 * 2. 从输出结果来看，在生产者调用notify()后，消费者并没有立即被唤醒，而是等到生产者退出同步块后才唤醒执行。（这点其实也好理解，synchronized同步方法（块）同一时刻只允许一个线程在里面，生产者不退出，消费者也进不去）
 * 3. 注意，消费者被唤醒后是从wait()方法（被阻塞的地方）后面执行，而不是重新从同步块开始。
 */
public class ThreadProducerConsumer {

    static final Object obj = new Object();  //对象锁

    private static boolean flag = false;

    public static void main(String[] args) throws Exception {

        Thread consume = new Thread(new Consume(), "Consume");
        Thread produce = new Thread(new Produce(), "Produce");
        consume.start();
        Thread.sleep(1000);
        produce.start();

//        try {
//            produce.join();
//            consume.join();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }

    // 生产者线程
    static class Produce implements Runnable {

        @Override
        public void run() {

            synchronized (obj) {
                System.out.println("进入生产者线程");
                System.out.println("生产");
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);  //模拟生产过程
                    flag = true;
                    obj.notify();  //通知消费者
                    TimeUnit.MILLISECONDS.sleep(1000);  //模拟其他耗时操作
                    System.out.println("退出生产者线程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //消费者线程
    static class Consume implements Runnable {

        @Override
        public void run() {
            synchronized (obj) {
                System.out.println("进入消费者线程");
                System.out.println("wait flag 1:" + flag);
                while (!flag) {  //判断条件是否满足，若不满足则等待
                    try {
                        System.out.println("还没生产，进入等待");
                        obj.wait();
                        System.out.println("结束等待");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("wait flag 2:" + flag);
                System.out.println("消费");
                System.out.println("退出消费者线程");
            }

        }
    }
}
